//********************************************************************
//	created:	14:2:2012   17:46
//	filename: 	selectsocketmanager.cpp
//	author:		tiamo
//	purpose:	select socket manager
//********************************************************************

#include "stdafx.h"
#include "selectsocketmanager.h"

namespace network
{
	//
	// build select set
	//
	SOCKET SelectSocketManager::buildSelectSet(fd_set& readSet, fd_set& writeSet, fd_set& errorSet)
	{
		//
		// lock
		//
		FD_ZERO(&readSet);
		FD_ZERO(&writeSet);
		FD_ZERO(&errorSet);
		utils::Locker locker(mSocketListLock);

		//
		// add to all sets
		//
		SOCKET maxFile														= 0;
		auto addToSet = [this, &maxFile, &readSet, &writeSet, &errorSet](std::pair<SOCKET, utils::SmartPointer<AsyncSocket>> const& thePair)
		{
			utils::SmartPointer<AsyncSocket> const& theSocket				= thePair.second;
			SOCKET handle													= theSocket ? theSocket->getHandle() : INVALID_SOCKET;
			if(handle == INVALID_SOCKET)
			{
				LogError(L"SocketManager(%p): invalid socket (%p)", this, theSocket.get());
			}
			else
			{
				maxFile														= std::max<SOCKET>(maxFile, handle);
				FD_SET(handle, &readSet);
				if(!theSocket->canSendBuffer())
				{
					FD_SET(handle, &writeSet);
				}
				FD_SET(handle, &errorSet);
			}
		};
		std::for_each(mSocketList.begin(), mSocketList.end(), addToSet);
		return maxFile;
	}

	//
	// build event set
	//
	std::vector<std::pair<utils::SmartPointer<AsyncSocket>, int>> SelectSocketManager::buildEventSet(fd_set& readSet, fd_set& writeSet, fd_set& errorSet)
	{
		//
		// build socket list
		//
		utils::Locker lock(mSocketListLock);
		std::vector<std::pair<utils::SmartPointer<AsyncSocket>, int>> eventSet;
		auto buildSocketList = [this, &readSet, &writeSet, &errorSet, &eventSet](std::pair<SOCKET, utils::SmartPointer<AsyncSocket>> const& thePair)
		{
			utils::SmartPointer<AsyncSocket> const& theSocket				= thePair.second;
			SOCKET handle													= theSocket->getHandle();
			bool readEvent													= !!FD_ISSET(handle, &readSet);
			bool writeEvent													= !!FD_ISSET(handle, &writeSet);
			bool errorEvent													= !!FD_ISSET(handle, &errorSet);
			if(readEvent || writeEvent || errorEvent)
				eventSet.push_back(std::make_pair(theSocket, (readEvent ? 4 : 0) + (writeEvent ? 2 : 0) + (errorEvent ? 1 : 0)));
		};
		std::for_each(mSocketList.begin(), mSocketList.end(), buildSocketList);

		return eventSet;
	}

	//
	// poll network event
	//
	void SelectSocketManager::pollNetworkEvent(uint32_t pollTimeout)
	{
		//
		// flush socket buffer
		//
		flushSocketBuffer();

		//
		// build select set
		//
		fd_set readSet, writeSet, errorSet;
		SOCKET maxFile														= buildSelectSet(readSet, writeSet, errorSet);
		if(!maxFile)
			return;

		//
		// do select, this call may failed if we pass an invalid handle to it
		//
		timeval	timeout;
		timeout.tv_sec														= pollTimeout / 1000;
		timeout.tv_usec														= (pollTimeout % 1000) * 1000;
		int fdCount															= select(static_cast<int>(maxFile + 1), &readSet, &writeSet, &errorSet, &timeout);
		if(fdCount <= 0)
			return;

		//
		// build event set
		//
		auto eventSet														= buildEventSet(readSet, writeSet, errorSet);

		//
		// call socket
		//
		auto callSocket = [this](std::pair<utils::SmartPointer<AsyncSocket>, int> const& theEvent)
		{
			bool readEvent													= theEvent.second & 4 ? true : false;
			bool writeEvent													= theEvent.second & 2 ? true : false;
			bool errorEvent													= theEvent.second & 1 ? true : false;

			onNetworkEvent(theEvent.first, readEvent, writeEvent, errorEvent);
		};
		std::for_each(eventSet.begin(), eventSet.end(), callSocket);
	}
}
